#include <os/driver.h>
#include <os/debug.h>
#include <os/initcall.h>
#include <os/safety.h>
#include <lib/type.h>
#include <lib/stdio.h>
#include <sys/ioctl.h>
#include <driver/view/view.h>
#include <driver/view/screen.h>
#include <driver/view/refresh.h>
#include <driver/view/render.h>
#include <driver/view/core.h>
#include <driver/view/env.h>
#include <driver/view/msg.h>
#include <driver/view/mouse.h>
#include <driver/view/bitmap.h>
#include <driver/view/env.h>
#include <driver/view.h>

static int view_open_count = 0;

static iostatus_t ViewDriverEnter(driver_object_t *driver)
{
    device_object_t *device;
    iostatus_t status = IO_SUCCESS;
    device_extension_t *extension;

    // init screen
    if (ViewScreenInit())
    {
        KPrint("[view] screen init err\n");
        status = IO_FAILED;
        return status;
    }

    int i;
    char devname[DEVICE_NAME_LEN+1];
    for (i = 0; i < VIEW_NUM_MAX; i++)
    {
        memset(devname, 0, DEVICE_NAME_LEN);
        sprintf(devname, "%s%d", DEVICE_NAME, i);
        status = IoCreateDevice(driver, sizeof(device_extension_t), devname, DEVICE_TYPE_VIEW, &device);
        if (status != IO_SUCCESS)
        {
            KPrint(PRINT_ERR "[view]: create device %s failed!\n", DEVICE_NAME);
            return status;
        }
        // neighter io mode
        device->flags = 0;
        extension = (device_extension_t *)device->device_extension;
        extension->view = NULL;
        extension->flags = 0;
    }
    return status;
}

static iostatus_t ViewDriverExit(driver_object_t *driver)
{
    device_object_t *device, *next;
    list_traversal_all_owner_to_next_safe(device, next, &driver->device_list, list)
    {
        IoDeleteDevice(device);
    }
    // del driver name
    string_del(&driver->name);
    return IO_SUCCESS;
}

static iostatus_t ViewOpen(device_object_t *device, io_request_t *ioreq)
{
    device_extension_t *extension = device->device_extension;
    iostatus_t status = IO_SUCCESS;
    int flags = ioreq->parame.open.flags;

    // init core
    if (!view_open_count)
    {
        if (ViewCoreInit() < 0)
        {
            KPrint("[view] init view driver core failed!\n");
            return IO_FAILED;
        }
    }

    if (!extension->view) // create view
    {
        int width = (flags >> 12) & 0x1fff;
        int height = (flags)&0x1fff;
        int type = (flags >> 26) & 0x1f;

        // new a view object
        extension->view = ViewCreate(0, 0, width, height, type);    

        if (!extension->view)
        {
            status = IO_FAILED;
        }
        else
        {
            ViewEnvSendToMonitor(extension->view, VIEW_MSG_CREATE, 0);
        }
    }

    view_open_count++;
    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    //KPrint("[view] device %s open ok\n", device->name.text);
    return status;
}

static int __ViewWriteMsg(view_msg_t *msg, int flags)
{
    if (msg->targe == VIEW_TARGE_NONE)
    {
        KPrint("[view] msg targe error!\n");
        return -1;
    }
    view_t *view = ViewFindById(msg->targe);
    if (!view)
    {
        KPrint("[view] view targe %d no found!\n", msg->targe);
        return -1;
    }
    // listen msg
    if (ViewMsgGetId(msg) == VIEW_MSG_SETICON)
    {
        // send to monitor
        ViewEnvSendToMonitor(view, VIEW_MSG_SETICON, msg->data1);
    }
    
    // send msg
    return ViewPutMsg(view, msg, flags);
}

static iostatus_t ViewRead(device_object_t *device, io_request_t *ioreq)
{
    device_extension_t *extension = device->device_extension;
    iostatus_t status = IO_SUCCESS;

    view_t *view = extension->view;
    if (view && ioreq->parame.read.len > 0)
    {
        if (ViewGetMsg(view, ioreq->user_buff, (extension->flags & DEVICE_NOWAIT) ? VIEW_MSG_NOWAIT : 0) < 0)
            status = IO_FAILED;
    }
    else
    {
        status = IO_FAILED;
    }
    ioreq->io_status.status = status;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t ViewWrite(device_object_t *device, io_request_t *ioreq)
{
    device_extension_t *extension = device->device_extension;
    iostatus_t status = IO_SUCCESS;

    view_t *view = extension->view;
    if (view && ioreq->parame.write.len > 0)
    {
        if (__ViewWriteMsg(ioreq->user_buff, (extension->flags & DEVICE_NOWAIT) ? VIEW_MSG_NOWAIT : 0) < 0)
        {
            status = IO_FAILED;
        }
    }
    else
    {
        status = IO_FAILED;
    }
    ioreq->io_status.status = status;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t ViewFastRead(device_object_t *device, size_t size,void *buff )
{
    device_extension_t *extension = device->device_extension;
    iostatus_t status = IO_SUCCESS;

    view_t *view = extension->view;
    if (view && size > 0)
    {
        if (ViewGetMsg(view, buff, (extension->flags & DEVICE_NOWAIT) ? VIEW_MSG_NOWAIT : 0) < 0)
        {
            status = IO_FAILED;
        }
        view_msg_t msg=*(view_msg_t*)buff;
    }
    else
    {
        status = IO_FAILED;
    }
    return status;
}

static iostatus_t ViewFastWrite(device_object_t *device,  size_t size,void *buff)
{
    device_extension_t *extension = device->device_extension;
    iostatus_t status = IO_SUCCESS;


    view_t *view = extension->view;
    if (view && size > 0)
    {
        if (__ViewWriteMsg(buff, (extension->flags & DEVICE_NOWAIT) ? VIEW_MSG_NOWAIT : 0) < 0)
        {
            status = IO_FAILED;
        }

    }
    else
    {
        status = IO_FAILED;
    }
    return status;
}

iostatus_t ViewClose(device_object_t *device, io_request_t *ioreq)
{
    device_extension_t *extension = device->device_extension;
    iostatus_t status = IO_SUCCESS;

    if (extension->view)
    {
        int z = extension->view->z;
        ViewHide(extension->view);
        if (ViewDestroy(extension->view) < 0)
        {
            ViewSetZ(extension->view, z);
            status = IO_FAILED;
            KPrint(PRINT_ERR "[view] view close %s failed!\n", DEVICE_NAME);
            goto end;
        }
        // send msg to monitor and exit monitor
        ViewEnvSendToMonitor(extension->view, VIEW_MSG_CLOSE, 0);
        ViewEnvCheckMonitorExit(extension->view);
        if (z >= 0) // reset
        {
            ViewEnvResetHoverAndActivity();
        }
        extension->view = NULL;
    }
    extension->flags = 0;

    --view_open_count;
    if (view_open_count < 0) // core exit
    {
        if (ViewCoreExit() < 0)
        {
            KPrint("[view] view core exit failed!\n");
            status = IO_FAILED;
        }
        view_open_count = 0;
    }
end:
    ioreq->io_status.status = status;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t __ViewDevCtl(device_extension_t *extension, uint64_t cmd, uint64_t arg)
{
    iostatus_t status = IO_SUCCESS;
    view_t *view = (view_t *)extension->view;
    switch (cmd)
    {
    case VIEWIO_SHOW:
        if (!view)
            status = IO_FAILED;
        else
        {
            if (!ViewShow(view))
            {
                //KPrint("[view] view show ok\n");
                ViewEnvSendToMonitor(view, VIEW_MSG_SHOW, 0);
                ViewEnvTryActivity(view);
            }
            else
                status = IO_FAILED;
        }
        break;
    case VIEWIO_HIDE:
        if (!view)
            status = IO_FAILED;
        else
        {
            if (!ViewHide(view))
            {
                //KPrint("[view] view hide ok\n");
                ViewEnvSendToMonitor(view, VIEW_MSG_HIDE, 0);
                ViewEnvResetHoverAndActivity();
            }
            else
                status = IO_FAILED;
        }
        break;
    case VIEWIO_SETPOS:
        if (!view)
            status = IO_FAILED;
        else
        {
            uint32_t pos = *(uint32_t *)arg; 
            ViewSetXY(view, ((uint32_t)pos >> 16) & 0xffff, ((uint32_t)pos & 0xffff));
        }
        break;
    case VIEWIO_GETPOS:
        if (!view)
            status = IO_FAILED;
        else
        {
            uint32_t pos = (view->x << 16) | view->y;
            *(uint32_t *)arg = pos;
        }
        break;
    case VIEWIO_GETFLAGS:
        *(uint32_t*)arg=extension->flags;
        break;
    case VIEWIO_SETFLAGS:
        extension->flags=*(uint32_t*)arg;
        break;
    case VIEWIO_WRBMP:
        if (!view)
            status = IO_FAILED;
        else
        {
            uview_io_t *viewio = (uview_io_t *)arg;
            ViewRenderBitblt(view, viewio->x, viewio->y, (view_bitmap_t *)&viewio->bmp, viewio->bx, viewio->by, viewio->bw, viewio->bh);
            if (viewio->refresh)
            {
                ViewRefresh(view, viewio->x, viewio->y, viewio->x + viewio->bw, viewio->y + viewio->bh);
            }
        }
        break;
    case VIEWIO_SETTYPE:
        if (!view)
        {
            status = IO_FAILED;
        }
        else
        {
            if (ViewSetType(view, *(int *)arg) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_GETTYPE:
        if (!view)
        {
            status = IO_FAILED;
        }
        else
        {
            *(int *)arg = ViewGetType(view);
        }
        break;
    case VIEWIO_REFRESH:
        if (!view)
        {
            status = IO_FAILED;
        }
        else
        {
            view_region_t *vreg = (view_region_t *)arg;
            ViewRefresh(view, vreg->left, vreg->top, vreg->right, vreg->bottom);
        }
        break;
    case VIEWIO_ADDATTR:
        if (!view)
            status = IO_FAILED;
        else
        {
            if (ViewAddAttr(view, *(int *)arg) < 0)
            {
                status = IO_FAILED;
            }
        }
        break;
    case VIEWIO_DELATTR:
        if (!view)
            status = IO_FAILED;
        else
        {
            if (ViewDelAttr(view, *(int *)arg) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_RESIZE:
        if (!view)
            status = IO_FAILED;
        else
        {
            uint32_t vsize = *(uint32_t *)arg;
            if (ViewEnvTryResizeEx(view, (vsize >> 16) & 0xffff, (vsize & 0xffff)) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_GETSCREENSZ:
        if (!view)
            status = IO_FAILED;
        else
        {
            *(int *)arg = ViewEnvGetScreenSize();
        }
        break;
    case VIEWIO_GETLASTPOS:
        if (!view)
            status = IO_FAILED;
        else
        {
            *(uint32_t *)arg = ViewEnvGetLastPos();
        }
        break;
    case VIEWIO_SETZ:
        if(!view)
            status=IO_FAILED;
        else 
        {
            uint32_t z=*(uint32_t*)arg;
            ViewSetZ(view,(int)z);
        }
        break;
    case VIEWIO_GETMOUSEPOS:
        if (!view)
            status = IO_FAILED;
        else
        {
            *(uint32_t *)arg = ViewEnvGetMousePos();
        }
        break;
    case VIEWIO_SETSIZEMIN:
        if (!view)
            status = IO_FAILED;
        else
        {
            uint32_t size_min = *(uint32_t *)arg;
            ViewSetSizeMin(view, (size_min >> 16) & 0xffff, (size_min & 0xffff));
        }
        break;
    case VIEWIO_SETDRAGREGION:
        if (!view)
        {
            status = IO_FAILED;
        }
        else
        {
            view_region_t *vreg = (view_region_t *)arg;
            ViewSetDragRegion(view, vreg);
        }
        break;
    case VIEWIO_SETMOUSESTATUS:
        if (!view)
            status = IO_FAILED;
        else
        {
            view_mouse_status_t mstatus = *(view_mouse_status_t *)arg;
            ViewMouseSetStatus(mstatus);
        }
        break;
    case VIEWIO_SETMOUSESTATUSINFO:
        if (!view)
            status = IO_FAILED;
        else
        {
            view_mouse_status_info_t sinfo = *(view_mouse_status_info_t *)arg;
            if (ViewMouseSetStatusInfo(sinfo) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_GETMOUSESTATUS:
        if (!view)
            status = IO_FAILED;
        else
        {
            view_mouse_status_t *mstatus = (view_mouse_status_t *)arg;
            if (ViewMouseGetStatus(mstatus) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_GETVID:
        if (!view)
            status = IO_FAILED;
        else
        {
            *(uint32_t *)arg = view->id;
        }
        break;
    case VIEWIO_ADDTIMER:
        if (!view)
            status = IO_FAILED;
        else
        {
            uint64_t interval = *(uint32_t *)arg;
            int tiemr_id = ViewEnvAddTimer(view, interval);
            if (tiemr_id < 0)
                status = IO_FAILED;
            *(uint32_t *)arg = tiemr_id;
        }
        break;
    case VIEWIO_DELTIMER:
        if (!view)
            status = IO_FAILED;
        else
        {
            uint64_t timer_id = *(uint32_t *)arg;
            if (ViewEnvDelTimer(view, timer_id) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_RESTARTTIMER:
        if (!view)
            status = IO_FAILED;
        else
        {
            viewio_timer_t *vio_timer = (viewio_timer_t *)arg;
            if (ViewEnvRestartTimer(view, vio_timer->timer_id, vio_timer->interval) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_SETMONITOR:
        if (!view)
            status = IO_FAILED;
        else
        {
            if (ViewEnvSetMonitor(view, *(uint32_t *)arg) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_SETWINMAXIMRECT:
        if (!view)
            status = IO_FAILED;
        else
        {
            if (ViewEnvSetWinmaximRect((view_rect_t *)arg) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_GETWINMAXIMRECT:
        if (!view)
            status = IO_FAILED;
        else
        {
            if (ViewEnvGetWinmaximRect((view_rect_t *)arg) < 0)
                status = IO_FAILED;
        }
        break;
    case VIEWIO_GETMOUSESTATUSINFO:
        if (!view)
            status = IO_FAILED;
        else
        {
            view_mouse_status_info_t *sinfo = (view_mouse_status_info_t *)arg;
            if (ViewMouseGetStatusInfo(sinfo) < 0)
                status = IO_FAILED;
        }
        break;
    default:
        status = IO_FAILED;
        break;
    }

      return status;
}

static iostatus_t ViewDevCtl(device_object_t *device, io_request_t *ioreq)
{
    device_extension_t *extension = device->device_extension;
    iostatus_t status = IO_SUCCESS;
    uint32_t cmd = ioreq->parame.devctl.code;
    uint32_t arg = ioreq->parame.devctl.arg;

    status = __ViewDevCtl(extension, cmd, arg);
    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t ViewFastIO(device_object_t *device, int cmd, int arg)
{
    device_extension_t *extension = device->device_extension;

    return __ViewDevCtl(extension, cmd, arg);
}
static iostatus_t ViewDriverFunc(driver_object_t *driver)
{
    iostatus_t status = IO_SUCCESS;

    driver->driver_enter = ViewDriverEnter;
    driver->driver_exit = ViewDriverExit;

    driver->dispatch_fun[IOREQ_OPEN] = ViewOpen;
    driver->dispatch_fun[IOREQ_CLOSE] = ViewClose;
    driver->dispatch_fun[IOREQ_READ] = ViewRead;
    driver->dispatch_fun[IOREQ_FASTREAD] = (void *)ViewFastRead;
    driver->dispatch_fun[IOREQ_WRITE] = ViewWrite;
    driver->dispatch_fun[IOREQ_FASTWRITE] = (void *)ViewFastWrite;
    driver->dispatch_fun[IOREQ_DEVCTL] = ViewDevCtl;
    driver->dispatch_fun[IOREQ_FASTIO] = ViewFastIO;

    string_new(&driver->name, DRIVER_NAME, DRIVER_NAME_LEN);

    return status;
}


static __init void ViewDriverEntry()
{
    if (DriverObjectCreate(ViewDriverFunc) < 0)
    {
        KPrint(PRINT_ERR "[driver] %s: create driver %s failed!\n", __func__, DRIVER_NAME);
    }
}

filter_initcall(ViewDriverEntry);
